﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace VBoxService
{
    class ServiceStatus : IServiceStatus
    {
        /// <summary>
        /// Event-Log Handler.
        /// </summary>
        private System.Diagnostics.EventLog oServiceEvents;

        /// <summary>
        /// Service-Configuration.
        /// </summary>
        private Settings oServiceSettings;

        /// <summary>
        /// Get the Status for a specific device.
        /// </summary>
        /// <param name="sController">(string) Controller-Name</param>
        /// <param name="sPort">(string) Port-No.</param>
        /// <param name="sDevice">(string) Device-No.</param>
        /// <returns>(string) Device-Status.</returns>
        public string getDriveStatus(string sController, string sPort, string sDevice)
        {
            /**********************************************
             * Initialize Environment.
             * */
            oServiceSettings = new Settings();
            oServiceSettings.Load();

            oServiceEvents = new EventLog();
            oServiceEvents.Source = "VBoxServiceSource";
            oServiceEvents.Log = "VBoxService";

            try
            {
                /**********************************************
                * Setup Process.
                * */
                ProcessStartInfo oStartInfo = new ProcessStartInfo();
                oStartInfo.FileName = oServiceSettings.VBoxManagePath;
                oStartInfo.Arguments = "showvminfo \"" + oServiceSettings.VBoxGuestName + "\"";
                oStartInfo.UseShellExecute = false;
                oStartInfo.ErrorDialog = false;
                oStartInfo.CreateNoWindow = false;
                oStartInfo.RedirectStandardOutput = true;
                oStartInfo.RedirectStandardInput = false;

                string sCurrentStatus = String.Empty;
                string sControllerStatusPattern = oServiceSettings.ODDController + " (" + oServiceSettings.ODDPort + ", " + oServiceSettings.ODDDevice + "):";

                Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);

                /**********************************************
                * Read process output.
                * */
                while (true && pVBoxServer.StandardOutput.EndOfStream == false)
                {
                    string sLine = pVBoxServer.StandardOutput.ReadLine();
                    if (sLine.Contains(sControllerStatusPattern))
                    {
                        sCurrentStatus = sLine.Remove(0, sControllerStatusPattern.Length);
                        oServiceEvents.WriteEntry("Current Status: " + sCurrentStatus, EventLogEntryType.Information);
                        //sCurrentStatus = sCurrentStatus.Remove(sCurrentStatus.IndexOf('('), (sCurrentStatus.Length - sCurrentStatus.IndexOf('(') - 1));
                        return sCurrentStatus;
                    }
                }

            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to check status: " + ex.Message, EventLogEntryType.Error);
            }

            return "Empty or Error";
        }

        /// <summary>
        /// Mount Guest's DVD-Drive.
        /// </summary>
        /// <param name="bIsPhysical">(bool) use [true] for physical-device passthrough or [false] for ISO-file.</param>
        /// <param name="sTargetPath">(string) Driveletter (physical drive; e.g. D:) or path to ISO-image.</param>
        /// <returns>[true] if request was processed.</returns>
        public bool mountDVD(bool bIsPhysical, string sTargetPath)
        {
            /**********************************************
             * Initialize Environment.
             * */
            oServiceSettings = new Settings();
            oServiceSettings.Load();

            oServiceEvents = new EventLog();
            oServiceEvents.Source = "VBoxServiceSource";
            oServiceEvents.Log = "VBoxService";

            try
            {
                if(bIsPhysical)
                {
                    // Derive Drive-Letter from Target-Path.
                    //string sDriveLetter = sTargetPath.Substring(0, 1);
                    //string sDriveLetter = sTargetPath.Remove(1, sTargetPath.Length - 2);

                    /**********************************************
                    * Setup Process.
                    * */
                    ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "storageattach \"" + oServiceSettings.VBoxGuestName + "\" --storagectl \"" + oServiceSettings.ODDController + "\" --port " + oServiceSettings.ODDPort + " --device " + oServiceSettings.ODDDevice + " --type dvddrive --medium host:" + sTargetPath + ":");
                    oStartInfo.UseShellExecute = true;
                    oStartInfo.ErrorDialog = false;
                    oStartInfo.CreateNoWindow = true;
                    oStartInfo.RedirectStandardOutput = false;
                    oStartInfo.RedirectStandardInput = false;

                    Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
                    oServiceEvents.WriteEntry("VBox Service mounted DVD-Drive \"" + sTargetPath + " for \"" + oServiceSettings.VBoxGuestName + "\"");
                }
                else
                {
                    /**********************************************
                    * Setup Process.
                    * */
                    ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "storageattach \"" + oServiceSettings.VBoxGuestName + "\" --storagectl \"" + oServiceSettings.ODDController + "\" --port " + oServiceSettings.ODDPort + " --device " + oServiceSettings.ODDDevice + " --type dvddrive --medium \"" + sTargetPath + "\"");
                    oStartInfo.UseShellExecute = true;
                    oStartInfo.ErrorDialog = false;
                    oStartInfo.CreateNoWindow = true;
                    oStartInfo.RedirectStandardOutput = false;
                    oStartInfo.RedirectStandardInput = false;

                    Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
                    oServiceEvents.WriteEntry("VBox Service mounted DVD-Drive \"" + sTargetPath + " for \"" + oServiceSettings.VBoxGuestName + "\"");
                }
                
                return true;
            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to mount DVD-Drive \"" + sTargetPath + "\": " + ex.Message, EventLogEntryType.Error);
            }

            return false;
        }

        /// <summary>
        /// Set Guest's Powermode.
        /// </summary>
        /// <param name="iTargetMode">(int) Target-Mode as Int16.</param>
        /// <returns>[true] if command could be sent properly.</returns>
        public bool setPowerMode(int iTargetMode)
        {
            /**********************************************
             * Initialize Environment.
             * */
            oServiceSettings = new Settings();
            oServiceSettings.Load();

            oServiceEvents = new EventLog();
            oServiceEvents.Source = "VBoxServiceSource";
            oServiceEvents.Log = "VBoxService";

            /**********************************************
            * Send requested command.
            * */
            switch (iTargetMode)
            {
                case 0:
                    if (PowerOff())
                        return true;
                    break;
                case 1:
                    if (SaveState())
                        return true;
                    break;
                case 2:
                    if (PauseResume())
                        return true;
                    break;
                case 6:
                    if (PowerOn())
                        return true;
                    break;
                default:
                    break;
            }

            return false;
        }

        /// <summary>
        /// Power-On Guest.
        /// </summary>
        /// <returns>[true] if process was sent to VBox properly.</returns>
        private bool PowerOn()
        {
            try
            {
                /**********************************************
                * Setup Process.
                * */
                ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "startvm \"" + oServiceSettings.VBoxGuestName + "\" --type headless");
                oStartInfo.UseShellExecute = true;
                oStartInfo.ErrorDialog = false;
                oStartInfo.CreateNoWindow = true;
                oStartInfo.RedirectStandardOutput = false;
                oStartInfo.RedirectStandardInput = false;

                Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
                oServiceEvents.WriteEntry("VBox Service Wrapper starting \"" + oServiceSettings.VBoxGuestName + "\"");
                
                while (!pVBoxServer.HasExited)
                    System.Threading.Thread.Sleep(0);

                return true;
            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to start VBoxService: " + ex.Message, EventLogEntryType.Error);
            }

            return false;
        }

        /// <summary>
        /// Power-Off Guest.
        /// </summary>
        /// <returns>[true] if process was sent to VBox properly.</returns>
        private bool PowerOff()
        {
            try
            {
                /**********************************************
                * Setup Process.
                * */
                ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "controlvm \"" + oServiceSettings.VBoxGuestName + "\" acpipowerbutton");
                oStartInfo.UseShellExecute = true;
                oStartInfo.ErrorDialog = false;
                oStartInfo.CreateNoWindow = true;
                oStartInfo.RedirectStandardOutput = false;
                oStartInfo.RedirectStandardInput = false;

                Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
                oServiceEvents.WriteEntry("VBox Service Wrapper stopped \"" + oServiceSettings.VBoxGuestName + "\"");

                System.Threading.Thread.Sleep(3000);

                return true;
            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to stop VBoxService: " + ex.Message, EventLogEntryType.Error);
            }

            return false;
        }

        /// <summary>
        /// Invert Guest's Pause/Resume-State.
        /// </summary>
        /// <returns>[true] if process was sent to VBox properly.</returns>
        private bool PauseResume()
        {
            if (getCurrentStatus() == 2)
                return Resume();
            else if (getCurrentStatus() == 6)
                return Pause();
            else
                return false;
        }

        /// <summary>
        /// Pause Guest.
        /// </summary>
        /// <returns>[true] if process was sent to VBox properly.</returns>
        private bool Pause()
        {
            try
            {
                /**********************************************
                * Setup Process.
                * */
                ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "controlvm \"" + oServiceSettings.VBoxGuestName + "\" pause");
                oStartInfo.UseShellExecute = true;
                oStartInfo.ErrorDialog = false;
                oStartInfo.CreateNoWindow = true;
                oStartInfo.RedirectStandardOutput = false;
                oStartInfo.RedirectStandardInput = false;

                Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
                oServiceEvents.WriteEntry("VBox Service Wrapper paused \"" + oServiceSettings.VBoxGuestName + "\"");

                while (!pVBoxServer.HasExited)
                    System.Threading.Thread.Sleep(0);

                return true;
            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to pause VBoxService: " + ex.Message, EventLogEntryType.Error);
            }

            return false;
        }

        /// <summary>
        /// Save Guest-State to disk and power off.
        /// </summary>
        /// <returns>[true] if process was sent to VBox properly.</returns>
        private bool SaveState()
        {
            try
            {
                /**********************************************
                * Setup Process.
                * */
                ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "controlvm \"" + oServiceSettings.VBoxGuestName + "\" savestate");
                oStartInfo.UseShellExecute = true;
                oStartInfo.ErrorDialog = false;
                oStartInfo.CreateNoWindow = true;
                oStartInfo.RedirectStandardOutput = false;
                oStartInfo.RedirectStandardInput = false;

                Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
                oServiceEvents.WriteEntry("VBox Service Wrapper saved \"" + oServiceSettings.VBoxGuestName + "\"");

                while (!pVBoxServer.HasExited)
                    System.Threading.Thread.Sleep(0);

                return true;
            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to savestate VBoxService: " + ex.Message, EventLogEntryType.Error);
            }

            return false;
        }

        /// <summary>
        /// Resume paused Guest.
        /// </summary>
        /// <returns>[true] if process was sent to VBox properly.</returns>
        private bool Resume()
        {
            try
            {
                /**********************************************
                * Setup Process.
                * */
                ProcessStartInfo oStartInfo = new ProcessStartInfo(oServiceSettings.VBoxManagePath, "controlvm \"" + oServiceSettings.VBoxGuestName + "\" resume");
                oStartInfo.UseShellExecute = true;
                oStartInfo.ErrorDialog = false;
                oStartInfo.CreateNoWindow = true;
                oStartInfo.RedirectStandardOutput = false;
                oStartInfo.RedirectStandardInput = false;

                Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);
                oServiceEvents.WriteEntry("VBox Service Wrapper paused \"" + oServiceSettings.VBoxGuestName + "\"");

                while (!pVBoxServer.HasExited)
                    System.Threading.Thread.Sleep(0);

                return true;
            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to pause VBoxService: " + ex.Message, EventLogEntryType.Error);
            }

            return false;
        }

        /// <summary>
        /// Send ACPI-Powerbutton-Signal to Guest.
        /// </summary>
        /// <returns>[true] if process was sent to VBox properly.</returns>
        /// <remarks>Using PowerOff()</remarks>
        private bool ACPIPower()
        {
            return PowerOff();
        }

        /// <summary>
        /// Get current Guest-Status.
        /// </summary>
        /// <returns>(int) Guest-Status (Powermode) as Int16.</returns>
        public int getCurrentStatus()
        {
            /**********************************************
             * Initialize Environment.
             * */
            Settings oServiceSettings = new Settings();
            oServiceSettings.Load();

            oServiceEvents = new EventLog();
            oServiceEvents.Source = "VBoxServiceSource";
            oServiceEvents.Log = "VBoxService";

            try
            {
                /**********************************************
                * Setup Process.
                * */
                ProcessStartInfo oStartInfo = new ProcessStartInfo();
                oStartInfo.FileName = oServiceSettings.VBoxManagePath;
                oStartInfo.Arguments = "showvminfo \"" + oServiceSettings.VBoxGuestName + "\"";
                oStartInfo.UseShellExecute = false;
                oStartInfo.ErrorDialog = false;
                oStartInfo.CreateNoWindow = false;
                oStartInfo.RedirectStandardOutput = true;
                oStartInfo.RedirectStandardInput = false;

                string sCurrentStatus = String.Empty;

                Process pVBoxServer = System.Diagnostics.Process.Start(oStartInfo);

                /**********************************************
                * Read process output.
                * */
                while (true && pVBoxServer.StandardOutput.EndOfStream == false)
                {
                    string sLine = pVBoxServer.StandardOutput.ReadLine();
                    if (sLine.Contains("State:"))
                    {
                        sCurrentStatus = sLine.Remove(0, 17);
                        oServiceEvents.WriteEntry("Current Status: " + sCurrentStatus, EventLogEntryType.Information);
                        sCurrentStatus = sCurrentStatus.Remove(sCurrentStatus.IndexOf('('), (sCurrentStatus.Length - sCurrentStatus.IndexOf('(') - 1));
                        break;
                    }
                }

                /******************************************
                 * Derrive Status from string
                 * */
                if (sCurrentStatus.ToLower().Contains("off"))
                    return 0;
                if (sCurrentStatus.ToLower().Contains("paused"))
                    return 2;
                if (sCurrentStatus.ToLower().Contains("saved"))
                    return 1;
                else if (sCurrentStatus.ToLower().Contains("running"))
                    return 6;
            }
            catch (Exception ex)
            {
                oServiceEvents.WriteEntry("Unable to check status: " + ex.Message, EventLogEntryType.Error);
                return 0;
            }

            return 1;
        }
    }
}
